/*
License

This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation; either version 2 of the License, or (at your option)
any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License (http://www.gnu.org/copyleft/gpl.html) for more details.

Original JavaScript Encoding/Decoding
	Written by Stephen Ostermiller
	Copyright (C) 2003-2005 Stephen Ostermiller (http://ostermiller.org/contact.pl?regarding=JavaScript+Encoding)

Actionscript 2.0 Port:
	Jason Nussbaum, September 2005
	Copyright (C) 2005 Jason Nussbaum (http://www.jasonnussbaum.com)
*/
package org.moyrax.nereyda.lib
{
	public class Base64
		extends Object
	{
	
		// all access to this class is through the
		//	static Encode and Decode methods.
//		private function Base64():void {}
	
	
			public static function Encode( str:String ) : String
			{
				var encoder:Base64 = new Base64();
				return encoder.encodeBase64( str );
			}
	
			public static function Decode( str:String ) : String
			{
				var decoder:Base64 = new Base64();
				return decoder.decodeBase64( str );
			}
			
			public static function StringReplaceAll( source:String, find:String, replacement:String ) : String
			{
				return source.split( find ).join( replacement );
			}
	
	
	
	///#region Private vars
	
		///#region Static Vars
	
			private static var _EndOfInput:int = -1;
	
			private static var _Chars:Array = new Array(
				'A','B','C','D','E','F','G','H',
				'I','J','K','L','M','N','O','P',
				'Q','R','S','T','U','V','W','X',
				'Y','Z','a','b','c','d','e','f',
				'g','h','i','j','k','l','m','n',
				'o','p','q','r','s','t','u','v',
				'w','x','y','z','0','1','2','3',
				'4','5','6','7','8','9','+','/'
			);
	
			private static var _CharsReverseLookup:Array; // = new Array();
			private static var _CharsReverseLookupInited:Boolean = InitReverseChars();
			private static var _Digits:Array = new Array( '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' );
	
	
		///#endregion
	
		private var _base64Str:String;
		private var _base64Count:Number;
	
	
	///#endregion
	
	
	///#region Private Mtehods
	
		///#region Static Methods
	
		private static function InitReverseChars() : Boolean
		{
			_CharsReverseLookup = new Array();
	
			for ( var i:int =0; i < _Chars.length; i++ )
			{
				_CharsReverseLookup[ _Chars[i] ] = i;
			}
	
			return true;
		}
	
		private static function UrlDecode( str:String ) : String
		{
			str = StringReplaceAll( str, "\+", " " );
			str = unescape( str );
			return str;
		}
	
		private static function UrlEncode( str:String ) : String
		{
			str = escape( str );
			str = StringReplaceAll( str, "\+", "%2B" );
			str = StringReplaceAll( str, "%20", "+" );
			return str;
		}
	
		///#endregion
	
		private function setBase64Str( str:String ):void
		{
			_base64Str = str;
			_base64Count = 0;
		}
	
		private function readBase64() : Number
		{
			if( !_base64Str )
			{
				return _EndOfInput;
			}
	
			if( _base64Count >= _base64Str.length )
			{
				return _EndOfInput;
			}
	
			var c:Number = _base64Str.charCodeAt( _base64Count ) & 0xff;
			_base64Count++;
	
			return c;
		}
	
		private function encodeBase64( str:String ):String
		{
			setBase64Str( str );
			var result:String = "";
			var inBuffer:Array = new Array(3);
			var lineCount:int = 0;
			var done:Boolean = false;
	
			while( !done && ( inBuffer[0] = readBase64() ) != _EndOfInput )
			{
				inBuffer[1] = readBase64();
				inBuffer[2] = readBase64();
	
				result += ( _Chars[ inBuffer[0] >> 2 ] );
	
			if( inBuffer[1] != _EndOfInput )
			{
					result += ( _Chars[ ( ( inBuffer[ 0 ] << 4 ) & 0x30 ) | ( inBuffer[ 1 ] >> 4 ) ] );
					if( inBuffer[ 2 ] != _EndOfInput )
					{
						result += ( _Chars[ ( ( inBuffer[ 1 ] << 2 ) & 0x3c ) | ( inBuffer[ 2 ] >> 6 ) ] );
						result += ( _Chars[ inBuffer[ 2 ] & 0x3F ] );
					}
					else
					{
						result += ( _Chars[ ( ( inBuffer[ 1 ] << 2 ) & 0x3c ) ] );
						result += ( "=" );
						done = true;
					}
				}
				else
				{
					result += ( _Chars[ ( ( inBuffer[ 0 ] << 4 ) & 0x30 ) ] );
					result += "=";
					result += "=";
					done = true;
				}
	
				lineCount += 4;
	
				if(lineCount >= 76)
				{
					result += ('\n');
					lineCount = 0;
				}
			}
			return result;
		}
	
		private function readReverseBase64():Object 
		{
			if( !_base64Str )
				{ return _EndOfInput; }
	
			while( true )
			{
				if( _base64Count >= _base64Str.length )
					{ return _EndOfInput; }
	
				var nextCharacter:String = _base64Str.charAt( _base64Count );
	
				_base64Count++;
	
				if( _CharsReverseLookup[ nextCharacter ] )
				{
					return _CharsReverseLookup[nextCharacter];
				}
	
				if( nextCharacter == 'A' )
					{ return '0'; }
			}
	
			return _EndOfInput;
		}
	
		private function ntos( n:Number ) : String
		{
			var str:String = n.toString( 16 ); // parseInt( n.toString(), 16 ).toString(); //
	
			if( str.length == 1 ) str = "0" + str;
			str = "%" + str;
	
			return unescape( str );
		}
	
		private function decodeBase64( str:String ) : String
		{
			setBase64Str(str);
			var result:String = "";
			var inBuffer:Array = new Array( 4 );
			var done:Boolean = false;
	
			while( !done && ( inBuffer[ 0 ] = readReverseBase64() ) != _EndOfInput
				&& ( inBuffer[ 1 ] = readReverseBase64() ) != _EndOfInput )
			{
				inBuffer[ 2 ] = readReverseBase64();
				inBuffer[ 3 ] = readReverseBase64();
	
				result += ntos( ( ( ( inBuffer[ 0 ] << 2 ) & 0xff ) | inBuffer[ 1 ] >> 4 ) );
	
				if( inBuffer[ 2 ] != _EndOfInput )
				{
					result += ntos( ( ( ( inBuffer[ 1 ] << 4 ) & 0xff ) | inBuffer[ 2 ] >> 2 ) );
					if(inBuffer[3] != _EndOfInput)
					{
						result +=  ntos((((inBuffer[2] << 6)  & 0xff) | inBuffer[3]));
					}
					else
					{
						done = true;
					}
				}
				else
				{
					done = true;
				}
			}
	
			return result;
		}
	
		private function toHex( n:Number ) : String
		{
			var result:String = "";
			var start:Boolean = true;
			for( var i:Number=32; i>0; )
			{
				i-=4;
				var digit:int = (n>>i) & 0xf;
				if(!start || digit != 0)
				{
					start = false;
					result += _Digits[digit];
				}
			}
			return ( result=="" ? "0" : result );
		}
	
		private function pad( str:String, len:Number, pad:String ) : String
		{
			var result:String = str;
			for (var i:int =str.length; i<len; i++){
				result = pad + result;
			}
			return result;
		}
	
		private function encodeHex( str:String ) : String
		{
			var result:String = "";
			for( var i:Number=0; i<str.length; i++)
			{
				result += pad( toHex( str.charCodeAt( i ) & 0xff ), 2, '0' );
			}
			return result;
		}
	
		private function decodeHex( str:String ) : String
		{
			//str = str.replace( new RegExp("s/[^0-9a-zA-Z]//g" ) );
	
			var result:String = "";
			var nextchar:String = "";
	
			for( var i:int=0; i<str.length; i++ )
			{
				nextchar += str.charAt(i);
	
				if(nextchar.length == 2)
				{
					result += ntos( parseInt( "0x" + nextchar) );
					nextchar = "";
				}
			}
			return result;
		}
	}
}